package cn.iocoder.yudao.module.system.service.exam;

import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.config.ExamTestProperties;
import cn.iocoder.yudao.module.system.controller.admin.exam.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.exam.*;
import cn.iocoder.yudao.module.system.dal.mysql.exam.*;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 测评数据存储 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class ExamDataServiceImpl implements ExamDataService {

    @Resource
    private ExamQuestionMapper examQuestionMapper;
    
    @Resource
    private ExamOptionMapper examOptionMapper;
    
    @Resource
    private ExamInfoMapper examInfoMapper;
    
    @Resource
    private ExamReportMapper examReportMapper;
    
    @Resource
    private ExamUserAnswerMapper examUserAnswerMapper;

    @Resource
    private ExamTestProperties examTestProperties;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveQuestions(String appKey, String tbl, List<ExamTestQuestionVO> questions) {
        if (questions == null || questions.isEmpty()) {
            return;
        }

        log.info("开始保存测评题目数据，appKey: {}, tbl: {}, 题目数量: {}", appKey, tbl, questions.size());

        // 先删除已存在的数据
        cleanExistingQuestions(appKey, tbl);

        // 保存新的题目数据
        List<ExamQuestionDO> questionDOs = new ArrayList<>();
        List<ExamOptionDO> optionDOs = new ArrayList<>();

        for (int i = 0; i < questions.size(); i++) {
            ExamTestQuestionVO questionVO = questions.get(i);
            
            // 构建题目DO
            ExamQuestionDO questionDO = ExamQuestionDO.builder()
                    .appKey(appKey)
                    .tbl(tbl)
                    .apiDetailsId(questionVO.getDetailsId())
                    .classifys(questionVO.getClassifys())
                    .title(questionVO.getTitle())
                    .details(questionVO.getDetails())
                    .marks(questionVO.getMarks())
                    .format(questionVO.getFormat())
                    .sort(i + 1)
                    .status(1)
                    .build();
            
            // 先插入题目以获取ID
            examQuestionMapper.insert(questionDO);
            questionDOs.add(questionDO);

            // 构建选项DO
            if (questionVO.getOptions() != null && !questionVO.getOptions().isEmpty()) {
                for (ExamTestOptionVO optionVO : questionVO.getOptions()) {
                    ExamOptionDO optionDO = ExamOptionDO.builder()
                            .questionId(questionDO.getId())
                            .apiDetailsId(questionVO.getDetailsId())
                            .optionIndex(optionVO.getIndex())
                            .content(optionVO.getContent())
                            .score(optionVO.getScore())
                            .optionKey(optionVO.getOptionKey())
                            .sort(optionVO.getIndex() + 1)
                            .status(1)
                            .build();
                    optionDOs.add(optionDO);
                }
            }
        }

        // 批量插入选项
        if (!optionDOs.isEmpty()) {
            examOptionMapper.insertBatch(optionDOs);
        }

        log.info("成功保存测评题目数据，题目数量: {}, 选项数量: {}", questionDOs.size(), optionDOs.size());
    }

    @Override
    public List<ExamTestQuestionVO> getQuestions(String tbl) {
        // 从配置中获取appKey
        String appKey = examTestProperties.getAppKeyByTbl(tbl);

        // 查询题目列表
        List<ExamQuestionDO> questionDOs = examQuestionMapper.selectByAppKeyAndTbl(appKey, tbl);
        if (questionDOs.isEmpty()) {
            return new ArrayList<>();
        }

        // 查询选项列表
        List<Long> questionIds = questionDOs.stream().map(ExamQuestionDO::getId).collect(Collectors.toList());
        List<ExamOptionDO> optionDOs = examOptionMapper.selectByQuestionIds(questionIds);
        
        // 按题目ID分组选项
        Map<Long, List<ExamOptionDO>> optionMap = optionDOs.stream()
                .collect(Collectors.groupingBy(ExamOptionDO::getQuestionId));

        // 转换为VO
        List<ExamTestQuestionVO> questions = new ArrayList<>();
        for (ExamQuestionDO questionDO : questionDOs) {
            ExamTestQuestionVO questionVO = BeanUtils.toBean(questionDO, ExamTestQuestionVO.class);
            questionVO.setDetailsId(questionDO.getApiDetailsId());

            // 设置选项
            List<ExamOptionDO> options = optionMap.get(questionDO.getId());
            if (options != null && !options.isEmpty()) {
                List<ExamTestOptionVO> optionVOs = BeanUtils.toBean(options, ExamTestOptionVO.class);
                questionVO.setOptions(optionVOs);
            }

            questions.add(questionVO);
        }

        return questions;
    }

    @Override
    public ExamQuestionDO getQuestionByApiDetailsId(Long apiDetailsId) {
        // 从配置中获取默认appKey（兼容性方法）
        String appKey = examTestProperties.getDefaultAppKey();
        return examQuestionMapper.selectByAppKeyAndApiDetailsId(appKey, apiDetailsId);
    }

    @Override
    public ExamQuestionDO getQuestionByApiDetailsIdAndTbl(Long apiDetailsId, String tbl) {
        // 从配置中获取对应tbl的appKey
        String appKey = examTestProperties.getAppKeyByTbl(tbl);
        return examQuestionMapper.selectByAppKeyAndApiDetailsIdAndTbl(appKey, apiDetailsId, tbl);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTestInfo(String appKey, String tbl, ExamTestInfoVO testInfo) {
        log.info("开始保存测评信息数据，appKey: {}, tbl: {}", appKey, tbl);

        // 先删除已存在的数据
        examInfoMapper.deleteByAppKeyAndTbl(appKey, tbl);

        // 构建测评信息DO
        ExamInfoDO infoDO = ExamInfoDO.builder()
                .appKey(appKey)
                .tbl(tbl)
                .name(testInfo.getName())
                .questionNum(testInfo.getQuestionNum())
                .examTotalTime(testInfo.getExamTotalTime())
                .examTips(testInfo.getExamTips())
                .poster(testInfo.getPoster())
                .introduceImages(testInfo.getIntroduceImages())
                .advertiseTitle(testInfo.getAdvertiseTitle())
                .introduce(testInfo.getIntroduce())
                .standardIntroduce(testInfo.getStandardIntroduce())
                .scoreFormat(testInfo.getScoreFormat())
                .fieldOptions(testInfo.getFieldOptions() != null ? JSON.toJSONString(testInfo.getFieldOptions()) : null)
                .fieldOptions2(testInfo.getFieldOptions2() != null ? JSON.toJSONString(testInfo.getFieldOptions2()) : null)
                .status(1)
                .build();

        examInfoMapper.insert(infoDO);
        log.info("成功保存测评信息数据");
    }

    @Override
    public ExamTestInfoVO getTestInfo(String tbl) {
        // 从配置中获取appKey
        String appKey = examTestProperties.getAppKeyByTbl(tbl);
        ExamInfoDO infoDO = examInfoMapper.selectByAppKeyAndTbl(appKey, tbl);
        if (infoDO == null) {
            return null;
        }

        ExamTestInfoVO testInfo = BeanUtils.toBean(infoDO, ExamTestInfoVO.class);
        
        // 解析JSON字段
        if (StrUtil.isNotBlank(infoDO.getFieldOptions())) {
            try {
                testInfo.setFieldOptions((List<Map<String, Object>>) (List<?>) JSON.parseArray(infoDO.getFieldOptions(), Map.class));
            } catch (Exception e) {
                log.warn("解析fieldOptions失败", e);
            }
        }

        if (StrUtil.isNotBlank(infoDO.getFieldOptions2())) {
            try {
                testInfo.setFieldOptions2((List<Map<String, Object>>) (List<?>) JSON.parseArray(infoDO.getFieldOptions2(), Map.class));
            } catch (Exception e) {
                log.warn("解析fieldOptions2失败", e);
            }
        }

        return testInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTestReport(Long userId, String appKey, String tbl, String testBatchNo, 
                              ExamAnswerSubmitReqVO submitReqVO, ExamTestReportVO report) {
        log.info("开始保存测评报告数据，userId: {}, testBatchNo: {}", userId, testBatchNo);

        // 构建答案字符串
        StringBuilder answerBuilder = new StringBuilder();
        if (submitReqVO.getAnswers() != null) {
            for (int i = 0; i < submitReqVO.getAnswers().size(); i++) {
                ExamAnswerSubmitReqVO.ExamAnswerItemVO answerItem = submitReqVO.getAnswers().get(i);
                if (i > 0) {
                    answerBuilder.append(",");
                }
                answerBuilder.append(answerItem.getDetailsId()).append("-").append(answerItem.getAnswerIndex());
            }
        }

        // 构建报告DO
        ExamReportDO reportDO = ExamReportDO.builder()
                .userId(userId)
                .appKey(appKey)
                .tbl(tbl)
                .testBatchNo(testBatchNo)
                .reportType(submitReqVO.getReportType())
                .answerString(answerBuilder.toString())
                .isAllAnswer(submitReqVO.getIsAllAnswer())
                .reportContent(JSON.toJSONString(report))
                .score(report.getScore())
                .myScoreArr(report.getMyScoreArr())
                .myScoreDetailsArr(report.getMyScoreDetailsArr())
                .appraise(report.getAppraise())
                .description(report.getDescription())
                .suitableJob(report.getSuitableJob())
                .referenceMajor(report.getReferenceMajor())
                .status(1)
                .build();

        examReportMapper.insert(reportDO);
        log.info("成功保存测评报告数据");
    }

    @Override
    public ExamTestReportVO getTestReport(Long userId, String testBatchNo) {
        ExamReportDO reportDO = examReportMapper.selectByUserIdAndBatchNo(userId, testBatchNo);
        if (reportDO == null) {
            return null;
        }

        // 解析报告内容
        if (StrUtil.isNotBlank(reportDO.getReportContent())) {
            try {
                return JSON.parseObject(reportDO.getReportContent(), ExamTestReportVO.class);
            } catch (Exception e) {
                log.warn("解析报告内容失败", e);
            }
        }

        // 如果解析失败，返回基本信息
        ExamTestReportVO report = new ExamTestReportVO();
        report.setScore(reportDO.getScore());
        report.setMyScoreArr(reportDO.getMyScoreArr());
        report.setMyScoreDetailsArr(reportDO.getMyScoreDetailsArr());
        report.setAppraise(reportDO.getAppraise());
        report.setDescription(reportDO.getDescription());
        report.setSuitableJob(reportDO.getSuitableJob());
        report.setReferenceMajor(reportDO.getReferenceMajor());
        return report;
    }

    @Override
    public ExamTestReportVO getLatestTestReport(Long userId) {
        ExamReportDO reportDO = examReportMapper.selectLatestByUserId(userId);
        if (reportDO == null) {
            return null;
        }

        return getTestReport(userId, reportDO.getTestBatchNo());
    }

    @Override
    public List<ExamTestReportVO> getUserTestHistory(Long userId) {
        List<ExamReportDO> reportDOs = examReportMapper.selectByUserId(userId);
        List<ExamTestReportVO> reports = new ArrayList<>();

        for (ExamReportDO reportDO : reportDOs) {
            ExamTestReportVO report = getTestReport(userId, reportDO.getTestBatchNo());
            if (report != null) {
                reports.add(report);
            }
        }

        return reports;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUserAnswers(Long userId, String testBatchNo, ExamAnswerSubmitReqVO submitReqVO) {
        log.info("开始保存用户答题记录，userId: {}, testBatchNo: {}", userId, testBatchNo);

        List<ExamUserAnswerDO> answerDOs = new ArrayList<>();
        
        if (submitReqVO.getAnswers() != null) {
            for (ExamAnswerSubmitReqVO.ExamAnswerItemVO answerItem : submitReqVO.getAnswers()) {
                // 查询题目信息 - 使用更精确的查询，包含tbl参数
                ExamQuestionDO questionDO = getQuestionByApiDetailsIdAndTbl(answerItem.getDetailsId(), submitReqVO.getTbl());
                if (questionDO == null) {
                    log.warn("未找到题目信息，apiDetailsId: {}, tbl: {}", answerItem.getDetailsId(), submitReqVO.getTbl());
                }

                // 查询选项信息 - 使用安全的查询方法
                ExamOptionDO optionDO = getOptionSafely(questionDO, answerItem.getDetailsId(), answerItem.getAnswerIndex());

                ExamUserAnswerDO answerDO = ExamUserAnswerDO.builder()
                        .userId(userId)
                        .testBatchNo(testBatchNo)
                        .questionId(questionDO != null ? questionDO.getId() : null)
                        .apiDetailsId(answerItem.getDetailsId())
                        .optionId(optionDO != null ? optionDO.getId() : null)
                        .answerIndex(answerItem.getAnswerIndex())
                        .answerContent(optionDO != null ? optionDO.getContent() : null)
                        .score(optionDO != null ? optionDO.getScore() : null)
                        .answerTime(LocalDateTime.now())
                        .status(1)
                        .build();
                
                answerDOs.add(answerDO);
            }
        }

        if (!answerDOs.isEmpty()) {
            examUserAnswerMapper.insertBatch(answerDOs);
        }

        log.info("成功保存用户答题记录，记录数量: {}", answerDOs.size());
    }

    @Override
    public List<ExamUserAnswerDO> getUserAnswers(Long userId, String testBatchNo) {
        return examUserAnswerMapper.selectByUserIdAndBatchNo(userId, testBatchNo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cleanExpiredData(String tbl) {
        // 从配置中获取appKey
        String appKey = examTestProperties.getAppKeyByTbl(tbl);
        log.info("开始清理过期数据，appKey: {}, tbl: {}", appKey, tbl);

        // 清理题目和选项数据
        cleanExistingQuestions(appKey, tbl);

        // 清理测评信息
        examInfoMapper.deleteByAppKeyAndTbl(appKey, tbl);

        log.info("完成清理过期数据");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUserTestRecord(Long userId, String testBatchNo) {
        log.info("开始删除用户测评记录，userId: {}, testBatchNo: {}", userId, testBatchNo);
        
        // 删除答题记录
        examUserAnswerMapper.deleteByUserIdAndBatchNo(userId, testBatchNo);
        
        // 删除测评报告
        examReportMapper.deleteByUserIdAndBatchNo(userId, testBatchNo);
        
        log.info("完成删除用户测评记录");
    }

    @Override
    public ExamTestReportVO getCachedTestReport(String appKey, String tbl, String reportType,
                                               String answerString, String isAllAnswer) {
        log.info("查询缓存的测评报告，appKey: {}, tbl: {}, reportType: {}, answerString: {}, isAllAnswer: {}",
                StrUtil.isNotBlank(appKey) ? appKey.substring(0, Math.min(8, appKey.length())) + "***" : "空",
                tbl, reportType,
                StrUtil.isNotBlank(answerString) ? answerString.substring(0, Math.min(50, answerString.length())) + "..." : "空",
                isAllAnswer);

        // 查询已存在的报告
        ExamReportDO reportDO = examReportMapper.selectByAnswerCombination(appKey, tbl, reportType, answerString, isAllAnswer);
        if (reportDO == null) {
            log.info("未找到缓存的测评报告");
            return null;
        }

        log.info("找到缓存的测评报告，报告ID: {}, 创建时间: {}", reportDO.getId(), reportDO.getCreateTime());

        // 解析报告内容
        if (StrUtil.isNotBlank(reportDO.getReportContent())) {
            try {
                ExamTestReportVO report = JSON.parseObject(reportDO.getReportContent(), ExamTestReportVO.class);
                log.info("成功从缓存获取测评报告");
                return report;
            } catch (Exception e) {
                log.warn("解析缓存报告内容失败，报告ID: {}", reportDO.getId(), e);
            }
        }

        // 如果解析失败，返回基本信息
        ExamTestReportVO report = new ExamTestReportVO();
        report.setScore(reportDO.getScore());
        report.setMyScoreArr(reportDO.getMyScoreArr());
        report.setMyScoreDetailsArr(reportDO.getMyScoreDetailsArr());
        report.setAppraise(reportDO.getAppraise());
        report.setDescription(reportDO.getDescription());
        report.setSuitableJob(reportDO.getSuitableJob());
        report.setReferenceMajor(reportDO.getReferenceMajor());
        log.info("从缓存获取测评报告基本信息");
        return report;
    }

    /**
     * 清理已存在的题目数据
     */
    private void cleanExistingQuestions(String appKey, String tbl) {
        // 查询已存在的题目
        List<ExamQuestionDO> existingQuestions = examQuestionMapper.selectByAppKeyAndTbl(appKey, tbl);
        if (!existingQuestions.isEmpty()) {
            List<Long> questionIds = existingQuestions.stream().map(ExamQuestionDO::getId).collect(Collectors.toList());

            // 删除选项
            examOptionMapper.deleteByQuestionIds(questionIds);

            // 删除题目
            examQuestionMapper.deleteByAppKeyAndTbl(appKey, tbl);
        }
    }

    /**
     * 安全地查询选项信息，避免重复数据导致的异常
     *
     * @param questionDO 题目信息
     * @param apiDetailsId 第三方API题目ID
     * @param answerIndex 选项索引
     * @return 选项信息
     */
    private ExamOptionDO getOptionSafely(ExamQuestionDO questionDO, Long apiDetailsId, Integer answerIndex) {
        try {
            ExamOptionDO optionDO = null;

            if (questionDO != null) {
                // 优先使用包含题目ID的精确查询
                try {
                    optionDO = examOptionMapper.selectByQuestionIdAndApiDetailsIdAndIndex(
                            questionDO.getId(), apiDetailsId, answerIndex);
                    if (optionDO != null) {
                        log.debug("通过精确查询找到选项信息，questionId: {}, apiDetailsId: {}, answerIndex: {}",
                                questionDO.getId(), apiDetailsId, answerIndex);
                        return optionDO;
                    }
                } catch (Exception e) {
                    log.warn("精确查询选项失败，questionId: {}, apiDetailsId: {}, answerIndex: {}, error: {}",
                            questionDO.getId(), apiDetailsId, answerIndex, e.getMessage());
                }
            }

            // 如果精确查询失败，使用原有查询方法作为备选
            try {
                optionDO = examOptionMapper.selectByApiDetailsIdAndIndex(apiDetailsId, answerIndex);
                if (optionDO != null) {
                    log.debug("通过备选查询找到选项信息，apiDetailsId: {}, answerIndex: {}", apiDetailsId, answerIndex);
                } else {
                    log.warn("未找到选项信息，apiDetailsId: {}, answerIndex: {}", apiDetailsId, answerIndex);
                }
                return optionDO;
            } catch (Exception e) {
                log.error("查询选项信息失败，apiDetailsId: {}, answerIndex: {}, error: {}",
                        apiDetailsId, answerIndex, e.getMessage(), e);
                return null;
            }

        } catch (Exception e) {
            log.error("查询选项信息时发生未知错误，apiDetailsId: {}, answerIndex: {}, error: {}",
                    apiDetailsId, answerIndex, e.getMessage(), e);
            return null;
        }
    }

}
